47장 에러처리
에러 처리의 필요성
발생한 에러를 대처하지 않고 방치하면 프로그램 강제 종료된다.
→ 에러처리가 중요하다.
try…catch 문을 이용해서 에러에 대응할 수 있다.
console.log('[Start]');
try {
foo();
} catch (error) {
console.error('[에러 발생]', error);
// [에러 발생] ReferenceError: foo is not defined
}
// 발생한 에러에 적절한 대응을 하면 프로그램이 강제 종료되지 않는다.
console.log('[End]');
그러나, 직접적인 에러를 발생하지 않는 예외적인 상황이 발생할 수도 있다.
예를 들면 에러를 발생시키지 않고 Null을 반환할 수도 있다.
// DOM에 button 요소가 존재하는 경우 querySelector 메서드는 에러를 발생시키지 않고 null을 반환한다.
const $button = document.querySelector('button'); // null
$button?.classList.add('disabled');
이 경우에는 단축평가 or 옵셔널 체이닝 연산자를 활용해서 에러를 처리할 수도 있을 것이다.
이렇게 다양한 상황에서 예외가 발생할 수 있고, 그것들에 대응하는 코드를 작성하는 것이 중요하다.
try … catch … finally 문
앞서 말한 것 처럼 에러처리 구현하는 방법은 크게 2가지가 있다.
- 예외적인 상황의 반환 값 (null or -1) 을 단축 평가 또는 옵셔널 체이닝 연산자로 처리
- try … catch … finally 문
try {
//실행할 코드(에러가 발생할 가능성이 있는 코드)
} catch (err) {
//try 코드 블록에서 에러가 발생하면 이 코드 블록의 코드가 실행된다.
//err에는 try 코드 블록에서 발생한 Error 객체가 전달된다.
} finally {
//에러 발생과 상관없이 반드시 한 번 실행된다.
}
Error 객체
Error 생성자 함수는 에러 객체를 생성. 에러를 설명하는 에러 메시지를 인수로 전달 할 수 있다.
const error = new Error('invalid');
에러 객체는 message
프로퍼티와 stack
프로퍼티를 갖는다.
message
: Error 생성자 함수에서 인수로 전달한 에러 메시지
stack
: 에러를 발생시킨 콜 스택의 호출 정보를 나타내는 문자열. 디버깅 목적으로 사용
JS에서는 7가지 에러 생성자 함수가 있음
모두 Error.prototype을 상속 받음.
1 @ 1; // SyntaxError: Invalid or unexpected token
foo(); // ReferenceError: foo is not defined
null.foo; // TypeError: Cannot read property 'foo' of null
new Array(-1); // RangeError: Invalid array length
decodeURIComponent('%'); // URIError: URI malformed
throw 문
에러 객체 생성한다고 에러가 발생하는 건 아님.
try {
// 에러 객체를 생성한다고 에러가 발생하는 것은 아니다.
new Error('something wrong');
} catch (error) {
console.log(error);
}
그럼 어떻게 에러를 발생시키는 건데?
→ throw 문으로 에러 객체를 던져야 한다.
try {
// 에러 객체를 던지면 catch 코드 블록이 실행되기 시작한다.
throw new Error('something wrong');
} catch (error) {
console.log(error);
}
코드를 보면 throw로 에러를 던지면 catch문에서 error에 에러 객체가 할당 된다.
예를 들어 콜백함수를 호출하는 Repeat 함수 안에 throw 에러문이 있다면 try 코드 블록 내에서 사용해야 한다
// 외부에서 전달받은 콜백 함수를 n번만큼 반복 호출한다
const repeat = (n, f) => {
// 매개변수 f에 전달된 인수가 함수가 아니면 TypeError를 발생시킨다.
if (typeof f !== 'function') throw new TypeError('f must be a function');
for (var i = 0; i < n; i++) {
f(i); // i를 전달하면서 f를 호출
}
};
try {
repeat(2, 1); // 두 번째 인수가 함수가 아니므로 TypeError가 발생(throw)한다.
} catch (err) {
console.error(err); // TypeError: f must be a function
}
에러의 전파
에러는 호출자 방향으로 전파된다.
→ 콜 스택의 아래 방향으로 전파.
const foo = () => {
throw Error('foo에서 발생한 에러'); // ④
};
const bar = () => {
foo(); // ③
};
const baz = () => {
bar(); // ②
};
try {
baz(); // ①
} catch (err) {
console.error(err);
}
foo에서 콜 스택 아래 방향으로 전파되고 위 코드에서는 전역에서 catch 된다.
throw된 에러를 어디에서도 catch 하지 않으면 프로그램은 강제 종료된다.